第一次独立开发 Golang 微服务工程的记录,方案设计、具体开发和细节对齐通通都被我踩了一遍坑(真有我的.jpg
目录 Table of Contents
方案
阶段产物
- 目标功能:该微服务实现的功能简要说明
- 模块范围:该微服务涉及的上下游及自身模块的业务范围
- 逻辑架构:整体或内部的架构设计
- 调用时序:程序工作的调用时序
- 流程判断:复杂部分的流程判断
- 接口设计:先出文档然后开发
- 数据库模型设计:数据库表
- 注意事项:一些技术细节或潜在影响因素
接口设计
遵循 RESTful 风格
- 主要涉及路径命名、参数位置和状态返回等约束
充分考虑参数属性
- 文本长度:
- Path:过长的参数不宜放在 path
- Query:不同浏览器和服务器限制各异,默认为 2048 个字节
- Header:若服务器不作限制,可认为无限制
- Body:若服务器不作限制,可认为无限制
- 特殊字符:只有 Path 和 Query 需要进行编码和解码
- 上游:浏览器会自动 urlencode 编码,其它需要手工 urlencode 编码
- 下游:一般服务器的 Web 框架已经做了 urldecode 处理
- 经验参考:
- Token:Path 和 Query 对文本长度有一定限制,Body 不一定符合 RESTful 风格,放在 Header 是比较合适的;但页面跳转请求时,Header 无法生效,此时放在 Query 更加方便
数据库模型设计
字段属性
- 主键:
- 字段应该唯一:存在重复不适合做主键
- 字段建立索引:内容太长不适合做主键
- 文本:
- 考虑长文本使用 varchar 和 text 的区别
更新方式
- 原地更新:
- 优点:节省空间
- 缺点:多入口的并发读写会互相影响
- 插入更新:
- 优点:多入口的并发读写会互相独立
- 缺点:浪费空间
索引建立
- 若出现长文本或数据少的情景:
- 长文本不建议建索引
- 数据少可以不建索引
开发
加密方法
- AES (CBC + PKCS7 + PBKDF2):对称加密算法,内部服务之间使用该加密方法,速度较快
- RSA (PKCS1v15 for pub_key + PKCS8 for pri_key):非对称加密算法,外部服务交互使用该加密方法,保密较强
更新数据
- 增量更新:先查询出原记录的结构体,若请求字段不为空则更新结构体并更新数据库
- 全量更新:直接使用请求的结构体更新数据库
排查错误
- 查看应用运行日志和数据库操作日志
异常处理
- Golang 使用 err 代替 expection,err 包含的错误更广泛,当希望对特定某类错误进行处理时,要注意做区分。如:使用 gorm.ErrRecordNotFound 或判断列表是否为空来区分找不到和其它异常。
空值处理
- Gin 的模型绑定中,当传入字段为空时结构体会赋默认值,希望忽略为空字段应该使用指针(https://github.com/gin-gonic/gin/issues/659)。
- Gorm 的操作中,若对空值有所限制,需要注意是否会产生副作用。
使用公共库
- 尽可能使用公共库,避免地重复造轮子
配置与常量
- 配置:根据环境修改变量(改变时无侵入)
- 常量:程序固有且基本不会改变(改变时须侵入)
中间件的使用
认证
1 | // 可以直接在路由组应用中间件 |
跨域
1 | // 必须在路由注册前应用才会生效 |
操作进行回滚
数据库层面
1 | // 进行事务回滚 |
外部调用层面
1 | // 创建失败后,删除脏数据 |
对齐
接口出错的判定依据
- 根据状态码还是错误码
同名参数的真实含义
- 有些参数看似同名,实际上代表的意思在不同的微服务中是不一样的